package com.gitee.huanminabc.jfastcache.memory;

import com.gitee.huanminabc.jfastcache.AbstractCache;
import com.gitee.huanminabc.jfastcache.CacheObj;
import lombok.extern.slf4j.Slf4j;

import java.lang.ref.SoftReference;
import java.util.Iterator;
import java.util.Map;
import java.util.PriorityQueue;


/**
 * 自动适配缓存清理:
 * <p>1. 优先时间过期和gc自动回收</p>
 * <p>2. 容量快满了时，清除没有使用的缓存 也就是访问次数为0的缓存</p>
 * <p>3. 容量满了时，清除最早访问的缓存,直到满足80%后停止</p>
 * <p>4. 以上策略每隔5分钟执行一次</p>
 * @param <V>
 */

@Slf4j
public abstract class AbstractMemoryCache<V> extends AbstractCache<V> {
    protected AbstractMemoryCache(int capacity) {
        super(capacity);

    }

    protected AbstractMemoryCache(int capacity, int timeout) {
        super(capacity, timeout);
    }

    /**
     * 确保新key有足够的容量空间
     * 如果容量超过120%，循环清理直到满足要求
     * 如果容量满了但未超过120%，也清理一次
     * 
     * @param key 要添加的key
     */
    private void ensureCapacityForNewKey(String key) {
        // 先检查是否是新key，避免不必要的容量检查
        boolean isNewKey = !cacheMap.containsKey(key);
        if (!isNewKey) {
            return; // 如果是已存在的key，不需要容量检查
        }
        
        int capacity120 = getCapacity120();
        int currentSize = cacheMap.size();
        
        // 如果容量超过120%，循环清理直到满足要求
        while (currentSize >= capacity120) {
            int sizeBefore = currentSize;
            prune();
            currentSize = cacheMap.size();
            // 防止无限循环，如果清理后大小没有变化，跳出
            if (currentSize >= sizeBefore) {
                break;
            }
        }
        
        // 如果满了但未超过120%，也清理一次
        if (currentSize >= capacity && currentSize < capacity120) {
            prune();
        }
    }

    /**
     * 检查并清理超过120%容量的缓存
     * 在添加新key后调用，确保容量不超过120%
     * 
     * @param key 刚添加的key
     */
    private void checkCapacityAfterPut(String key) {
        // 只对新key进行检查
        if (!cacheMap.containsKey(key)) {
            return; // key不存在，可能是并发情况，跳过检查
        }
        
        int currentSize = cacheMap.size();
        int capacity120 = getCapacity120();
        
        // 如果超过120%，再次清理一次
        if (currentSize > capacity120) {
            prune();
        }
    }

    @Override
    public CacheObj<String, V> putInside(String key, V value) {
        // 注意：timeout在构造函数中已检查，这里不需要重复检查
        CacheObj<String, V> stringVCacheObj = new CacheObj<>(key, value);
        //timeout=-1表示永久存活，负数也当作永久处理
        if (timeout <= -1) {
            stringVCacheObj.setTtl(-1);
        } else {
            stringVCacheObj.setTtl(timeout + System.currentTimeMillis());
        }
        
        // 确保新key有足够的容量空间
        ensureCapacityForNewKey(key);
        
        // 放入缓存（新key或更新已存在的key）
        SoftReference<CacheObj<String, V>> softReference = new SoftReference<>(stringVCacheObj);
        cacheMap.put(key, softReference);
        
        // 添加后再次检查容量
        checkCapacityAfterPut(key);
        
        return stringVCacheObj;
    }

    @Override
    public CacheObj<String, V> putInside(String key, V value, long timeout) {
        // timeout必须>0或=-1，不允许timeout=0
        if (timeout == 0) {
            throw new IllegalArgumentException("timeout不能为0，必须>0或=-1");
        }
        CacheObj<String, V> stringVCacheObj = new CacheObj<>(key, value);
        //timeout=-1表示永久存活，负数也当作永久处理
        if (timeout <= -1) {
            stringVCacheObj.setTtl(-1);
        } else {
            stringVCacheObj.setTtl(timeout + System.currentTimeMillis());
        }
        
        // 确保新key有足够的容量空间
        ensureCapacityForNewKey(key);
        
        // 放入缓存（新key或更新已存在的key）
        SoftReference<CacheObj<String, V>> softReference = new SoftReference<>(stringVCacheObj);
        cacheMap.put(key, softReference);
        
        // 添加后再次检查容量
        checkCapacityAfterPut(key);
        
        return stringVCacheObj;
    }

    public void putInside(CacheObj<String, V> cacheObj) {
        // 确保新key有足够的容量空间
        ensureCapacityForNewKey(cacheObj.getKey());
        
        // 放入缓存（新key或更新已存在的key）
        SoftReference<CacheObj<String, V>> softReference = new SoftReference<>(cacheObj);
        cacheMap.put(cacheObj.getKey(), softReference);
        
        // 添加后再次检查容量
        checkCapacityAfterPut(cacheObj.getKey());
    }

    @Override
    public V getInside(String key) {
        SoftReference<CacheObj<String, V>> cacheObjSoftReference = cacheMap.get(key);
        if (cacheObjSoftReference == null) {
            return null;
        }
        CacheObj<String, V> stringVCacheObj = cacheObjSoftReference.get();
        if (stringVCacheObj == null) {
            return null;
        }
        //先判断是否过期，如果过期则不更新统计信息
        if (stringVCacheObj.isExpired()){
            cacheMap.remove(key);
            return null;
        }
        //未过期才更新访问统计
        stringVCacheObj.accessCount.incrementAndGet();
        stringVCacheObj.lastAccess = System.currentTimeMillis();
        return stringVCacheObj.getObj();
    }

    @Override
    public V removeInside(String key) {
        SoftReference<CacheObj<String, V>> remove = cacheMap.remove(key);
        if (remove == null) {
            return null;
        }
        CacheObj<String, V> stringVCacheObj = remove.get();
        if (stringVCacheObj == null) {
            return null;
        }
        return stringVCacheObj.getObj();
    }

    @Override
    public void clearInside() {
        cacheMap.clear();
    }

    protected  synchronized void prune() {

        Iterator<Map.Entry<String, SoftReference<CacheObj<String, V>>>> iterator = cacheMap.entrySet().iterator();
        while (iterator.hasNext()) {
            Map.Entry<String, SoftReference<CacheObj<String, V>>> entry = iterator.next();
            SoftReference<CacheObj<String, V>> value = entry.getValue();
            //1.gc回收   清理value为null的 ,一但value为null，说明已经被gc回收了
            CacheObj<String, V> stringVCacheObj = value.get();
            if (stringVCacheObj == null) {
                iterator.remove();
                continue;
            }
            //跳过小于0的
            if (stringVCacheObj.getTtl() < 0) {
                continue;
            }

            //2.先清理缓存过期的
            if (stringVCacheObj.isExpired()) {
                iterator.remove();
                continue;
            }

            //3.如果内存快满了，清理没有使用的缓存
            // 缓存size检查，避免重复调用isNearlyFull()
            int currentSizeForNearlyFull = cacheMap.size();
            if (currentSizeForNearlyFull >= (capacity * 0.95)) {
                //删除访问次数为0的
                if (stringVCacheObj.accessCount.get() == 0) {
                    iterator.remove();
                }
            }
            stringVCacheObj = null;
        }

        //4.如果内存满了或超过120%,按照访问时间来排序，删除最早访问的,直到满足80%
        int currentSize = cacheMap.size();
        int capacity120 = getCapacity120();
        if (currentSize >= capacity || currentSize > capacity120) {
            //使用优先级队列，只保留需要删除的数量，避免全排序 O(n log n) -> O(n log k)
            int targetSize = Math.max(1, (int)(capacity * 0.80)); // 至少保留1个，避免容量很小时全部清理
            int needDeleteCount = currentSize - targetSize;
            if (needDeleteCount > 0) {
                PriorityQueue<Map.Entry<String, SoftReference<CacheObj<String, V>>>> priorityQueue = 
                    new PriorityQueue<>(needDeleteCount + 1, (o1, o2) -> {
                        CacheObj<String, V> obj1 = o1.getValue().get();
                        CacheObj<String, V> obj2 = o2.getValue().get();
                        // 如果对象被GC回收，优先删除
                        if (obj1 == null && obj2 == null) {
                            return 0;
                        }
                        if (obj1 == null) {
                            return -1;
                        }
                        if (obj2 == null) {
                            return 1;
                        }
                        return Long.compare(obj1.getLastAccess(), obj2.getLastAccess());
                    });
                
                // 缓存是否超过120%的判断，避免重复计算
                boolean overCapacity120 = currentSize > capacity120;
                //遍历所有条目，维护一个大小为 needDeleteCount 的最大堆（实际是最小访问时间）
                // 注意：即使缓存是永久的（ttl < 0），如果容量超过120%，也应该清理一些
                for (Map.Entry<String, SoftReference<CacheObj<String, V>>> entry : cacheMap.entrySet()) {
                    CacheObj<String, V> cacheObj = entry.getValue().get();
                    if (cacheObj == null) {
                        continue;
                    }
                    // 如果容量超过120%，即使是永久缓存也要清理
                    boolean shouldInclude = overCapacity120 || cacheObj.getTtl() >= 0;
                    if (!shouldInclude) {
                        continue;
                    }
                    if (priorityQueue.size() < needDeleteCount) {
                        priorityQueue.offer(entry);
                    } else {
                        Map.Entry<String, SoftReference<CacheObj<String, V>>> top = priorityQueue.peek();
                        if (top != null) {
                            CacheObj<String, V> topObj = top.getValue().get();
                            if (topObj != null && cacheObj.getLastAccess() < topObj.getLastAccess()) {
                                priorityQueue.poll();
                                priorityQueue.offer(entry);
                            }
                        }
                    }
                }
                
                //删除最早访问的，缓存targetSize避免重复计算
                int targetSize80 = (int)(capacity * 0.80);
                while (!priorityQueue.isEmpty()) {
                    Map.Entry<String, SoftReference<CacheObj<String, V>>> entry = priorityQueue.poll();
                    cacheMap.remove(entry.getKey());
                    // 缓存size检查，避免每次调用isHalfFull()
                    if (cacheMap.size() <= targetSize80) {
                        break;
                    }
                }
            }
        }
    }


}
